package A3D_Test 
{
	import _codec.versions.version1.a3d.CodecA3D;
	import alternativa.types.Float;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.ProgressEvent;
	import flash.net.Socket;
	import A3D_Test.Logger;
	import A3D_Test.ServerGateLoginResultEvent;
	import flash.utils.ByteArray;
	import flash.utils.Endian;

	public class ServerGate extends EventDispatcher
	{
		public const PktId_Null					:int = 0;
		
		public const PktId_Sv_LoginResult		:int = 100;
		public const PktId_Sv_ObjPos			:int = 101;
		
		public const PktId_Cl_Login				:int = 200;
		public const PktId_Cl_Pos				:int = 201;

		
		
		public function ServerGate( loginStr:String )
		{
			login = loginStr;
			
			sock = new Socket( "localhost", 2345 );
			sock.endian = Endian.BIG_ENDIAN;
			sock.addEventListener( Event.CONNECT, onSockConnected );
			sock.addEventListener( ProgressEvent.SOCKET_DATA, onSockData );
		}
		
		
		private function onSockConnected( event:Event ):void
		{
			Logger.Log( "Connected to server, sending login" );
			protoSendLogin();
		}
		
		
		private function onSockData( event:ProgressEvent ):void
		{
			Logger.Log( "Got data in socket" );
			
			while ( true )
			{
				if ( readState == ReadState_Failure )
					break;

				if ( readState == ReadState_PktLen ) {
					if ( sock.bytesAvailable < 4 )
						break;
					expectedPktLength = sock.readInt();
					if ( expectedPktLength < 4 ) { //PktId is mandatory
						Fail( "Packet is too small" );
						break;
					}
					if ( expectedPktLength > 4096 ) {
						Fail( "Packet is too big" );
						break;
					}
					readState = ReadState_PktId;
				}
				
				if ( readState == ReadState_PktId ) {
					if ( sock.bytesAvailable < expectedPktLength )
						break;
					currentPktId = sock.readInt();
					readState = ReadState_PktBody;
					expectedPktLength -= 4;
				}

				if ( readState == ReadState_PktBody ) {
					if ( sock.bytesAvailable < expectedPktLength )
						break;
					ReadSinglePacket();
					readState = ReadState_PktLen;
				}
			}
		}
		
		
		
		private function Fail( errMsg:String ):void
		{
			readState = ReadState_Failure;
			dispatchEvent( new ServerGateFailureEvent( errMsg ) );
		}
		
		
		private function ReadSinglePacket():Boolean
		{
			switch ( currentPktId )
			{
				case PktId_Null:
					Fail( "Wrong pkt id" );
					break;
					
				case PktId_Sv_LoginResult:
					if ( sock.bytesAvailable < 4 ) {
						Fail( "Data expected" );
						break;
					}
					var loginResult:int = sock.readInt();
					dispatchEvent( new ServerGateLoginResultEvent( loginResult ) );
					break;
					
				case PktId_Sv_ObjPos:
					if ( sock.bytesAvailable < 12 ) {
						Fail( "Data expected" );
						break;
					}
					var id:Number = sock.readInt();
					var x:Number = sock.readFloat();
					var y:Number = sock.readFloat();
					var z:Number = sock.readFloat();
					dispatchEvent( new ServerGateObjectPosEvent( id, x, y, z ) );
					break;
					
				default:
					var buf:ByteArray;
					sock.readBytes( buf );
					break;
			}
			
			return false;
		}
		
		
		
		private function protoSendLogin():void
		{
			var i:int; 
			for ( i = 0; i < 32; i++ ) {
				var c:Number = login.charCodeAt( i );
				if ( c < 32 || c > 128 )
					return;
			}
			if ( login.length > 8 )
				login = login.substr( 0, 8 );

			sock.writeInt( login.length + 4 );
			sock.writeInt( PktId_Cl_Login );
			sock.writeUTFBytes( login );
			sock.flush();
		}
		
		
		public function protoSendCoords( x:Number, y:Number, z:Number ):void
		{
			sock.writeInt( 16 );
			sock.writeInt( PktId_Cl_Pos );
			sock.writeFloat( x );
			sock.writeFloat( y );
			sock.writeFloat( z );
			sock.flush();
		}

		
		internal const ReadState_Failure		:int = -1;
		internal const ReadState_PktId 			:int = 0;
		internal const ReadState_PktLen 		:int = 1;
		internal const ReadState_PktBody 		:int = 2;

		internal var login:String;
		internal var sock:Socket;
		internal var readState:int = ReadState_PktLen;
		internal var currentPktId:int = PktId_Null;
		internal var expectedPktLength:int = 0;
	}
}
